4.1 Виджеты. Stateless и Stateful
3 из 3 шагов пройдено

 Виджеты. Stateless и Stateful

➡️Скачать презентацию. Flutter Stateful
➡️Ссылка на репозиторий с кодом этого урока

Подготовка

  1. Создаём новую git ветку с названием 4.1-Stateless-Stateful
  2. В директории lib создадим новую папку stateful_widgets 
  3. Добавим туда новый файл  с названием s1_stateful_widget.dart

Stateless Виджет

Сделаем виджет музыкального трека, где можно поставить Like.
После нажатия на иконку сердечка, музыкальный трек добавляется в избранное, а иконка закрашивается в красный цвет. Сделаем верстку и добавим кнопку-иконку

Файл s1_stateful_widget.dart

import 'package:flutter/material.dart';

class StateExample extends StatelessWidget {  
  const StateExample({super.key});  
  
  @override  
  Widget build(BuildContext context) {  
    return Card(  
      child: ListTile(  
        leading: Image.asset("assets/images/gu.webp"),  
        title: Text("Stateless"),  
        subtitle: Text("Flutter Vibes".toUpperCase()),  
        trailing: IconButton(  
          onPressed: () {},  
          icon: Icon(Icons.favorite_border),  
        ),  
      ),  
    );  
  }  
}

Файл main.dart 

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "Flutter Course",
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
      ),
      home: Scaffold(
        body: Container(
          padding: EdgeInsets.symmetric(horizontal: 32),
          decoration: BoxDecoration(
            gradient: LinearGradient(
              colors: [Color(0xFFBFF098), Color(0xFF6FD6FF)],
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
            ),
          ),
          child: Center(
            child: StateExample(),
          ),
        ), 
      ),
    );
  }
}

Добавим логику обработки нажатия на кнопку

Нужна булева переменная, которая будет хранить состояние кнопки лайка (нажата или не нажата).
Если кнопка нажата, то красим её в красный цвет, иначе убираем цвет.

Файл s1_stateful_widget.dart

import 'package:flutter/material.dart';


class StateExample extends StatelessWidget {  
  const StateExample({super.key});  
  
  @override  
  Widget build(BuildContext context) {  
    // Нажали на лайк или нет  
    bool isLiked = false;  
  
    return Card(  
      child: ListTile(  
        leading: Image.asset("assets/images/gu.webp"),  
        title: Text("Stateless"),  
        subtitle: Text("Flutter Vibes".toUpperCase()),  
        trailing: IconButton(  
          onPressed: () {  
            // Меняем значение на противоположное  
            isLiked = !isLiked;  
          },  
          // Если лайк поставлен то красный цвет, иначе стандартный
          icon: isLiked  
              ? Icon(  Icons.favorite, color: Colors.red)  
              : Icon(Icons.favorite_border),  
        ),  
      ),  
    );  
  }  
}

Нажимаем кнопку и ... ничего не происходит! 😢Почему?

Дело в том что StateExample это StatelessWidget, виджет без сохранения состояния!
Т.е. при изменении состояния кнопки (нажата/не нажата) ничего не происходит, потому что виджет не умеет работать с состоянием и реагировать на его изменение.

Нужно использовать второй тип виджетов во Flutter это Stateful виджеты!
Такие виджеты отслеживают изменения состояния и запускают перерисовку виджета, чтобы обновить интерфейс.

Stateful Виджет

Чтобы переделать StatelessWidget в StatefulWidget нужно написать два новых класса

  • Класс наследник StatefulWidget
  • Класс наследник State

Используем сниппеты в VSCode для создания StatefulWidget или

  1. Нажимаем правой кнопкой мыши  на StatelessWidget
  2. Выбираем Refactor…
  3. Выбираем Convert to StatefulWidget

Visual Studio Code

Используем сниппеты в Android Studio для StatefulWidget или

  1. Нажимаем alt + Enter на StatelessWidget
  2. Выбираем Convert to StatefulWidget

Android Studio

Как правильно обновлять состояние виджета

  • Использовать StatefulWidget
  • Вызвать специальный метод setState()
  • Вызов этого метода, заставляет автоматически запустить метод build()
  • Метод build() перестраивает/перерисовывает виджет на экране

Рефакторинг stateless виджета

Состояние isLiked нужно перенести за пределы метода build() !!!

Потому что, если оставить его в методе build(), то при каждом вызове build() в переменной isLiked будет одно и тоже значение false, что не имеет смысла.

🔥После изменения состояния нужно обязательно вызывать метод setState()

Файл s1_stateful_widget.dart

import 'package:flutter/material.dart';

class StateExample extends StatefulWidget {
  const StateExample({super.key});  

  @override
  State<StateExample> createState() => _StateExampleState();
}

class _StateExampleState extends State<StateExample> {
 bool isLiked = false; // За предлами метода build

  @override
  Widget build(BuildContext context) {
    return Card(
      child: ListTile(
        leading: Image.asset("assets/images/gu.webp"),
        title: Text("Stateless"),
        subtitle: Text("Flutter Vibes".toUpperCase()),
        trailing: IconButton(
          onPressed: () {
            // Вызываем setState() для обновления интерфейса
            setState(() { 
               isLiked = !isLiked;
            });
          },
          icon: isLiked
              ? Icon(Icons.favorite, color: Colors.red)
              : Icon(Icons.favorite_border),
        ),
      ),
    );
  }
}

setState()

Cообщает фреймворку, что состояние изменилось и нужно перерисовать UI.
Технически заставляет виджет вызвать снова метод build()

StatefulWidget

Это тип виджета во Flutter, который позволяет сохранять и обновлять состояние во время жизненного цикла приложения. Это означает, что виджет может изменять свое поведение и внешний вид в зависимости от изменения в его состоянии.

UI = f(state)

UI : верстка на экране
f : методы build()
state : состояние приложения

  • Stateless widgets (без сохранения/управления состоянием) - они имутабельны (НЕ ИЗМЕНЯЕМЫЕ). Имутабельные объекты - их свойства нельзя изменить, все значения final

  • Stateful widgets (с сохранением/управлением состояния) наоборот ИЗМЕНЯЕМЫЕ, они содержат state (состояние приложения), которое со временем может изменяться!

Изолированные Stateful виджеты

Flutter очень умный фреймворк.

  1. Если мы создадим несколько виджетов StateExample, то у каждого будет своё состояние.
  2. Перерисовыватья будет только тот виджет, где изменяется состояние.

Проверим сколько раз будет срабатывать метод build()
Временно добавим в него функцию debugPrint()

@override  
Widget build(BuildContext context) {  
  debugPrint("Update UI");  
  return Card(...);  
}

При первом построении экрана "Update UI" вызвалось 4 раза, потому что 4 карточки.
При изменении состояния у одной из карточек, в консоль пишется только 1 "Update UI".
Значит обновляется только конкретный виджет, а не весь экран.


Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий